﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Sockets;

namespace Up2Link.Core
{
    /// <summary>
    /// 内部类，被类CTcpConnection使用
    /// 封装了网络消息收发功能。
    /// 使用异步方式收发网络消息，并通过截获IOException来获知网络通信中断。
    /// </summary>
    internal class CTransBytes
    {
        /// <summary>
        /// 传输网络流的对象
        /// </summary>
        private NetworkStream ns = null;

        /// <summary>
        /// 存储接收到的消息的队列
        /// </summary>
        private Queue<Byte[]> recvMsg = new Queue<Byte[]>();

        /// <summary>
        /// 存储等待发送的消息的队列
        /// </summary>
        private Queue<Byte[]> sendMsg = new Queue<Byte[]>();

        /// <summary>
        /// 消息头，所有消息必须带有4字节的消息头表示消息的总长度（这里的总长度包括4字节的消息头）
        /// </summary>
        private Byte[] header = new Byte[4];

        /// <summary>
        /// 消息体，跟在消息头之后的消息内容
        /// </summary>
        private Byte[] data = null;

        /// <summary>
        /// 表示是否正在接收消息
        /// </summary>
        private Boolean receiving = false;

        /// <summary>
        /// 表示是否正在发送消息
        /// </summary>
        private Boolean sending = false;

        /// <summary>
        /// 表示网络流是否运作正常
        /// </summary>
        private Boolean streamOn = false;

        /// <summary>
        /// 构造函数
        /// 作为参数的网络流对象由CTcpConnection类创建，然后传递给构造函数
        /// </summary>
        /// <param name="ns">网络流</param>
        public CTransBytes(NetworkStream ns)
        {
            this.ns = ns;
            streamOn = true;
        }

        /// <summary>
        /// 字节传输逻辑函数
        /// </summary>
        public void Update()
        {
            if (!ns.CanRead || !ns.CanWrite)
            {
                streamOn = false;
            }

            //使用异步方式接收消息
            if (streamOn && !receiving)
            {
                receiving = true;
                try
                {
                    //异步调用，ReceiveCallBack为回调函数
                    ns.BeginRead(header, 0, 4, new AsyncCallback(ReceiveCallBack), null);
                }
                catch (Exception e)
                {
                    //如果捕捉到异常，则关闭网络流
                    streamOn = false;
                    //System.Console.WriteLine(e.StackTrace);
                }
            }

            //使用异步方式发送消息
            if (streamOn && !sending && sendMsg.Count > 0 )
            {
                sending = true;
                Byte[] msg = sendMsg.Dequeue();

                try
                {
                    //异步调用，SendCallBack为回调函数
                    ns.BeginWrite(msg, 0, msg.Length, new AsyncCallback(SendCallBack), null);
                }
                catch (Exception e)
                {
                    //如果捕捉到异常，则关闭网络流
                    streamOn = false;
                    //System.Console.WriteLine(e.StackTrace);
                }
            }
        }

        /// <summary>
        /// 返回网络流状态标记
        /// </summary>
        /// <returns>网络流状态标记</returns>
        public Boolean IsStreamOn()
        {
            return streamOn;
        }

        /// <summary>
        /// 关闭网络流
        /// </summary>
        public void Close()
        {
            streamOn = false;

            //等待消息发送完成
            while (sending) ;

            recvMsg.Clear();
            sendMsg.Clear();
            ns.Close();
        }

        /// <summary>
        /// 返回网络流接收到的消息
        /// </summary>
        /// <returns>返回队列的第一条消息，如果队列为空则返回null</returns>
        public Byte[] Receive()
        {
            if (recvMsg.Count > 0)
            {
                return recvMsg.Dequeue();
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// 将一条消息压入队列
        /// </summary>
        /// <param name="data">一条消息</param>
        public void Send(Byte[] data)
        {
            sendMsg.Enqueue(data);
        }

        /// <summary>
        /// 接收消息的回调函数
        /// </summary>
        /// <param name="rHeader">前一步异步调用的结果</param>
        private void ReceiveCallBack(IAsyncResult rHeader)
        {
            try
            {
                //如果回调函数执行时，网络流已经被关闭，则什么也不做
                if (streamOn)
                {
                    //读取4字节消息头
                    Int32 numHeaderRead = ns.EndRead(rHeader);
                    if (numHeaderRead == 4)
                    {
                        //计算消息总长度并创建字节数组
                        Int32 dataByteCount = BitConverter.ToInt32(header, 0);
                        data = new Byte[dataByteCount + 4];
                        header.CopyTo(data, 0);

                        //接收剩下的所有消息内容
                        Int32 dataRead = ns.Read(data, 4, dataByteCount);
                        recvMsg.Enqueue(data);
                    }
                    else
                    {
                        streamOn = false;
                    }
                }
            }
            catch (Exception e)
            {
                //如果捕捉到异常，则关闭网络流
                streamOn = false;
                //System.Console.WriteLine(e.StackTrace);
            }
            finally
            {
                receiving = false;
            }
        }

        /// <summary>
        /// 发送消息的回调函数
        /// </summary>
        /// <param name="r">前一步异步调用的结果</param>
        private void SendCallBack(IAsyncResult r)
        {
            try
            {
                //如果回调函数执行时，网络流已经被关闭，则什么也不做
                if (streamOn)
                {
                    //发送消息
                    ns.EndWrite(r);
                }
            }
            catch (Exception e)
            {
                //如果捕捉到异常，则关闭网络流
                streamOn = false;
                //System.Console.WriteLine(e.StackTrace);
            }
            finally
            {
                sending = false;
            }
        }
    }
}
